#!/usr/bin/env python

"""Test show how is elected a leader in a n-cycle graph.
This aproach does it in nlgn time and messages.
Every vertex sends the message to both of his neighbours. 
The message is either vertex ID or "ELECTED" string.
Usage: python leader_election2.py [n]"""

import sys

import logging
import networkx as nx
import nxchaos as nxc

#_______________________________________________________________________________

NODES = 10
nxc.setup_log('debug')
_logger = logging.getLogger('nxchaos.test')

#_______________________________________________________________________________

class LeaderElection2(nxc.algorithm.IAlgorithm):

    def run(self):
        # Begining the message sending rounds:
        i_am_the_leader = False
        leader = -1
        elected_mess_counter = 0 # to check if messages 'elected' from left and right have been received
        self.send(0, [self.id, self.id])
        self.send(1, [self.id, self.id])
        yield

        while True:
            if len(self.inbox) > 0:
                _logger.debug("[{id}] inbox:{inbox}".format(id=self.id, inbox=self.inbox))

            for port, message in self.inbox:
                # if port is 0 then send to port nr 1; if 1 then to 0
                forward = ~ port % 2
                who = message[0]
                what = message[1]
                # someone was elected, lets investigate who!
                if what == "elected":
                    if who != self.id:
                        leader = who
                        _logger.info("[{id}] The leader is: {leader}".format(id=self.id, leader=leader))
                        self.send(forward, [who, what])

                        if self.get_data() is None:
                            self.save_data([port, message])

                        elected_mess_counter += 1
                        if elected_mess_counter == 2:
                            return

                    else:
                        _logger.info("[{id}] All nodes informed.".format(id=self.id))
                        return
                    
                elif who == self.id and what == self.id:
                    # message returned, so... I'm the leader
                    _logger.info("[{id}] I'm the leader.".format(id=self.id))
                    i_am_the_leader = True
                    self.send(0, [self.id, "elected"])
                    self.send(1, [self.id, "elected"])

                    # don't have to read next messages
                    break
                    
                elif what > self.id and who != self.id:
                    self.send(forward, [who, what])

            yield

#_______________________________________________________________________________

if __name__ == "__main__":

    if len(sys.argv) > 1:
        NODES = int(sys.argv[1])

    _logger.info("Nodes in a graph: {0}".format(NODES))

    gfactory = nxc.graph.GraphFactory()
    graph = gfactory.from_builtin("cycle", n=NODES)
    

    network = nxc.network.Network(graph, LeaderElection2)
    network.save_partial_data(True)
    network.prepare()


    while network.is_working():
        _logger.info("Round nr {0}".format(network.round_number()))
        network.round()

    _logger.info("Saved data:")
    for worker in network:
        _logger.info("id:{0}, data:{1}".format(worker.id_random, worker.get_data()))

    _logger.info("Saving calculated data to file...")
    network.save_data()

